# Offline 3x-ui installation for ARMv7 / RouterOS 7.
# Existing containers and databases are never deleted.
# Container package and device-mode activation are prerequisites.

# ---------- SETTINGS ----------
:local imageFile "3x-ui-mikrotik-armv7.tar.gz"
:local containerName "3x-ui-mikrotik-armv7"
:local rootDir "3xui-root"

:local bridgeName "3xui-bridge"
:local vethName "veth1"

:local gatewayIP "172.17.0.1"
:local gatewayCIDR "172.17.0.1/24"
:local containerIP "172.17.0.2"
:local containerCIDR "172.17.0.2/24"
:local containerNetwork "172.17.0.0/24"

:local lanNetwork "192.168.88.0/24"
:local lanList "LAN"
:local wanList "WAN"
:local panelPort 2053

# ---------- PREFLIGHT ----------
:log info "3XUI: Checking prerequisites"

:if ([/system resource get architecture-name] != "arm") do={
    :error "3XUI: ARMv7 image requires an ARM router"
}

:local packageId [/system package find where name="container"]

:if ([:len $packageId] = 0) do={
    :error "3XUI: Install the Container package first"
}

:if ([/system package get $packageId disabled] = true) do={
    :error "3XUI: Enable Container package and reboot first"
}

:if ([/system device-mode get container] != true) do={
    :error "3XUI: Enable Container in device-mode first"
}

:if ([:len [/interface list find where name=$lanList]] = 0) do={
    :error "3XUI: LAN interface list is missing"
}

:if ([:len [/interface list find where name=$wanList]] = 0) do={
    :error "3XUI: WAN interface list is missing"
}

:if ([:len [/interface list member find where list=$lanList]] = 0) do={
    :error "3XUI: LAN interface list has no members"
}

:if ([:len [/interface list member find where list=$wanList]] = 0) do={
    :error "3XUI: WAN interface list has no members"
}

:local containerId [/container find where name=$containerName]

:if ([:len $containerId] > 1) do={
    :error "3XUI: Multiple matching containers found"
}

# Preserve an existing installation without changing its network.
:if ([:len $containerId] = 1) do={
    :log info "3XUI: Existing installation found; preserving network and data"
} else={

    # Check storage and uploaded file before making changes.
    :if ([:len [/file find where name=$imageFile]] = 0) do={
        :error "3XUI: Upload the image file to Files first"
    }

    :if ([/system resource get free-hdd-space] < 360000000) do={
        :error "3XUI: Insufficient free storage for extraction"
    }

    :if ([:len [/file find where name=$rootDir]] > 0) do={
        :error "3XUI: Root directory already exists; inspect it first"
    }

    :if ([:len [/file find where name=("/" . $rootDir)]] > 0) do={
        :error "3XUI: Root directory already exists; inspect it first"
    }

    # Do not take over an existing interface.
    :if ([:len [/interface find where name=$vethName]] > 0) do={
        :error "3XUI: veth1 already exists; inspect its configuration first"
    }

    :if ([:len [/interface find where name=$bridgeName]] > 0) do={
        :error "3XUI: Installation bridge already exists; inspect it first"
    }

    # Detect an existing address in the selected /24 network.
    :if ([:len [/ip address find where network="172.17.0.0"]] > 0) do={
        :error "3XUI: Selected subnet already exists on this router"
    }

    # ---------- NETWORK ----------
    :log info "3XUI: Creating bridge and veth"

    /interface bridge add \
        name=$bridgeName \
        comment="3XUI container network"

    /ip address add \
        address=$gatewayCIDR \
        interface=$bridgeName \
        comment="3XUI gateway"

    /interface veth add \
        name=$vethName \
        address=$containerCIDR \
        gateway=$gatewayIP \
        comment="3XUI container interface"

    /interface bridge port add \
        bridge=$bridgeName \
        interface=$vethName

    # ---------- OUTBOUND NAT ----------
    :if ([:len [/ip firewall nat find where comment="3XUI outbound NAT"]] = 0) do={
        /ip firewall nat add \
            chain=srcnat \
            src-address=$containerIP \
            out-interface-list=$wanList \
            action=masquerade \
            comment="3XUI outbound NAT"
    }

    # ---------- FIREWALL ----------
    # Rules are moved to the beginning so a later Drop does not block them.
    # Management access is limited to the configured LAN and panel port.

    :if ([:len [/ip firewall filter find where comment="3XUI LAN panel"]] = 0) do={
        /ip firewall filter add \
            chain=forward \
            in-interface-list=$lanList \
            src-address=$lanNetwork \
            dst-address=$containerIP \
            protocol=tcp \
            dst-port=$panelPort \
            action=accept \
            comment="3XUI LAN panel"

        /ip firewall filter move \
            [find where comment="3XUI LAN panel"] 0
    }

    :if ([:len [/ip firewall filter find where comment="3XUI outbound"]] = 0) do={
        /ip firewall filter add \
            chain=forward \
            in-interface=$bridgeName \
            src-address=$containerIP \
            out-interface-list=$wanList \
            action=accept \
            comment="3XUI outbound"

        /ip firewall filter move \
            [find where comment="3XUI outbound"] 0
    }

    :if ([:len [/ip firewall filter find where comment="3XUI incoming replies"]] = 0) do={
        /ip firewall filter add \
            chain=forward \
            dst-address=$containerIP \
            out-interface=$bridgeName \
            connection-state=established,related \
            action=accept \
            comment="3XUI incoming replies"

        /ip firewall filter move \
            [find where comment="3XUI incoming replies"] 0
    }

    :if ([:len [/ip firewall filter find where comment="3XUI panel replies"]] = 0) do={
        /ip firewall filter add \
            chain=forward \
            src-address=$containerIP \
            dst-address=$lanNetwork \
            out-interface-list=$lanList \
            connection-state=established,related \
            action=accept \
            comment="3XUI panel replies"

        /ip firewall filter move \
            [find where comment="3XUI panel replies"] 0
    }

    # ---------- IMPORT ----------
    :log info "3XUI: Importing uploaded image"

    /container add \
        name=$containerName \
        file=$imageFile \
        interface=$vethName \
        root-dir=$rootDir \
        start-on-boot=yes \
        logging=yes

    :set containerId [/container find where name=$containerName]
}

# ---------- WAIT AND START ----------
:if ([:len $containerId] != 1) do={
    :error "3XUI: Cannot identify the container"
}

/container set $containerId start-on-boot=yes logging=yes

:local ready false

# Wait up to 15 minutes for extraction.
:for attempt from=1 to=180 do={
    :local state [/container get $containerId status]

    :if (($state = "stopped") || ($state = "running")) do={
        :set ready true
        :break
    }

    :if ($state = "error") do={
        :error "3XUI: Import failed; check container logs"
    }

    :delay 5s
}

:if ($ready = false) do={
    :error "3XUI: Extraction timeout; check logs and free storage"
}

:if ([/container get $containerId status] = "stopped") do={
    :log info "3XUI: Starting container"
    /container start $containerId
}

:delay 10s

:if ([/container get $containerId status] != "running") do={
    :error "3XUI: Container is not running; check container logs"
}

:log info "3XUI: Container is running; automatic startup enabled"
:put "3XUI container is running."

:put "Check the panel protocol, port and web path in container logs."
:put "For a new installation with default settings, try:"
:put ("http://" . $containerIP . ":" . $panelPort . "/")